﻿using System;
using System.Windows;
using System.Collections.Generic;

namespace Silvery.Scene
{
    /// <summary>
    /// A星寻路系统
    /// </summary>
    public class AStarPathFinder:IDisposable
    {
        private class AStarNode
        {
            // Methods
            public AStarNode()
            {
            }
            public void Clear()
            {
                int num1;
                bool flag1;
                this.g = num1 = 0;
                this.f = num1;
                this.inOpenList = flag1 = false;
                this.inCloseList = flag1;
                this.father = null;
            }
            // Fields
            public bool block;
            public int f;
            public AStarNode father;
            public int g;
            public bool inCloseList;
            public int index;
            public bool inOpenList;
            public int x;
            public int y;
        }
        private class MinHeap
        {
            // Methods
            public MinHeap()
            {
                this.data = new List<AStarNode>();
            }
            public void Clear()
            {
                List<AStarNode>.Enumerator enumerator1 = this.data.GetEnumerator();
                try
                {
                    while (enumerator1.MoveNext())
                    {
                        enumerator1.Current.Clear();
                    }
                }
                finally
                {
                    enumerator1.Dispose();
                }
                this.data.Clear();
            }
            public void ModifValue(AStarNode c, int newValue)
            {
                int num1 = c.f;
                c.f = newValue;
                if (newValue < num1)
                {
                    this.MoveUp(c.index);
                }
                else if (newValue > num1)
                {
                    this.MoveDown(c.index);
                }
            }
            private void MoveDown(int index)
            {
                int num2;
                int num1 = this.data.Count;
            Label_000C:
                num2 = index * 2;
                int num3 = (index * 2) + 1;
                if ((num3 < num1) && (this.data[num2].f > this.data[num3].f))
                {
                    if (this.data[index].f <= this.data[num3].f)
                    {
                        return;
                    }
                    this.Swap(index, num3);
                    index = num3;
                    goto Label_000C;
                }
                if ((num2 < num1) && (this.data[index].f > this.data[num2].f))
                {
                    this.Swap(index, num2);
                    index = num2;
                    goto Label_000C;
                }
            }
            private void MoveUp(int index)
            {
                while (index > 1)
                {
                    int num1 = index / 2;
                    if (this.data[index].f < this.data[num1].f)
                    {
                        this.Swap(index, num1);
                    }
                    index = num1;
                }
            }
            public AStarNode Pop()
            {
                AStarNode node1 = this.data[1];
                this.Swap(1, this.data.Count - 1);
                this.data.RemoveAt(this.data.Count - 1);
                this.MoveDown(1);
                return node1;
            }
            public void Push(AStarNode t)
            {
                t.index = this.data.Count;
                this.data.Add(t);
                this.MoveUp(t.index);
            }
            private void Swap(int i1, int i2)
            {
                int num1 = this.data[i1].index;
                this.data[i1].index = this.data[i2].index;
                this.data[i2].index = num1;
                AStarNode node1 = this.data[i1];
                this.data[i1] = this.data[i2];
                this.data[i2] = node1;
            }

            // Properties
            public int Count
            {
                get
                {
                    return (this.data.Count - 1);
                }
            }
            // Fields
            private List<AStarNode> data;
        }
        private List<AStarNode> closeList;
        private AStarNode[,] map;
        private int mapH;
        private int mapW;
        private MinHeap openList;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="w"></param>
        /// <param name="h"></param>
        public AStarPathFinder(int w, int h)
        {
            this.closeList = new List<AStarNode>();
            this.openList = new MinHeap();
            this.mapW = w;
            this.mapH = h;
            this.map = new AStarNode[w, h];
            for (int num1 = 0; num1 < h; num1++)
            {
                for (int num2 = 0; num2 < w; num2++)
                {
                    AStarNode node1 = new AStarNode();
                    node1.x = num2;
                    node1.y = num1;
                    this.map[num2, num1] = node1;
                }
            }
        }
        /// <summary>
        /// 销毁方法
        /// </summary>
        public void Dispose()
        {
            if (openList != null)
            {
                openList.Clear();
                openList = null;
            }
            if (closeList != null)
            {
                closeList.Clear();
                closeList = null;
            }
            map = null;

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void Block(int x, int y)
        {
            this.map[x, y].block = true;
        }
        /// <summary>
        /// 
        /// </summary>
        public static int maxFinderW = 50;
        /// <summary>
        /// 
        /// </summary>
        public static int maxFinderH = 40;
        private int _rangeMinX = 0;
        private int _rangeMinY = 0;
        private int _rangeMaxX = 1;
        private int _rangeMaxY = 1;
        private int _sx
        {
            set { _rangeMinX = value - maxFinderW; _rangeMaxX = value + maxFinderW; }
        }
        private int _sy
        {
            set { _rangeMinY = value - maxFinderH; _rangeMaxY = value + maxFinderH; }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sx"></param>
        /// <param name="sy"></param>
        /// <param name="ex"></param>
        /// <param name="ey"></param>
        /// <returns></returns>
        public List<Point> GetPath(int sx, int sy, int ex, int ey)
        {
            _sx = sx; _sy = sy;
            List<Point> list1 = new List<Point>();
            if ((((sx < this.mapW) && (ex < this.mapW)) && ((sy < this.mapH) && (ey < this.mapH))) && (((sx >= 0) && (ex >= 0)) && ((sy >= 0) && (ey >= 0))))
            {
                this.openList.Clear();
                this.closeList.Clear();
                AStarNode node1 = this.map[sx, sy];
                node1.f = (Math.Abs((int)(ex - sx)) + Math.Abs((int)(ey - sy))) * 10;
                while (node1 != null)
                {
                    node1.inCloseList = true;
                    this.closeList.Add(node1);
                    if ((node1.x == ex) && (node1.y == ey))
                    {
                        break;
                    }
                    if (this.Transact(node1, -1, 0, 10, ex, ey))
                    {
                        this.Transact(node1, -1, -1, 14, ex, ey);
                        this.Transact(node1, -1, 1, 14, ex, ey);
                    }
                    if (this.Transact(node1, 1, 0, 10, ex, ey))
                    {
                        this.Transact(node1, 1, -1, 14, ex, ey);
                        this.Transact(node1, 1, 1, 14, ex, ey);
                    }
                    this.Transact(node1, 0, -1, 10, ex, ey);
                    this.Transact(node1, 0, 1, 10, ex, ey);
                    if (this.openList.Count > 0)
                    {
                        node1 = this.openList.Pop();
                        continue;
                    }
                    node1 = null;
                }
                if (this.closeList.Count <= 0)
                {
                    return list1;
                }
                AStarNode node2 = this.closeList[this.closeList.Count - 1];
                if ((node2.x != ex) || (node2.y != ey))
                {
                    int num1 = 0xfffffff;
                    for (int num2 = this.closeList.Count - 1; num2 >= 0; num2--)
                    {
                        AStarNode node3 = this.closeList[num2];
                        if (node3.f < num1)
                        {
                            num1 = node3.f;
                            node2 = node3;
                        }
                    }
                }
                while (node2 != null)
                {
                    Point point2 = new Point();
                    Point point1 = point2;
                    point1.X = node2.x * GlobleConfig.SceneGridSize.Width;
                    point1.Y = node2.y * GlobleConfig.SceneGridSize.Height;
                    list1.Add(point1);
                    node2 = node2.father;
                }
                list1.Reverse();
                List<AStarNode>.Enumerator enumerator1 = this.closeList.GetEnumerator();
                try
                {
                    while (enumerator1.MoveNext())
                    {
                        enumerator1.Current.Clear();
                    }
                }
                finally
                {
                    enumerator1.Dispose();
                }
                this.closeList.Clear();
                this.openList.Clear();
            }
            return list1;
        }
        //做了改造，需要限定范围
        private bool Transact(AStarNode father, int off_x, int off_y, int price, int ex, int ey)
        {
            off_x += father.x;
            off_y += father.y;
            if ((off_x < 0) || (off_x >= this.mapW) || (off_x < _rangeMinX) || (off_x >= _rangeMaxX))
            //if ((off_x < _rangeMinX) || (off_x >= _rangeMaxX))
            {
                return false;
            }
            if ((off_y < 0) || (off_y >= this.mapH) || (off_y < _rangeMinY) || (off_y >= _rangeMaxY))
            //if ((off_y < _rangeMinY) || (off_y >= _rangeMaxY))
            {
                return false;
            }
            if (this.map[off_x, off_y].block)
            {
                return false;
            }
            AStarNode node1 = this.map[off_x, off_y];
            if (!node1.inCloseList)
            {
                if (!node1.inOpenList)
                {
                    node1.father = father;
                    node1.g = father.g + price;
                    node1.f = node1.g + ((Math.Abs((int)(ex - off_x)) + Math.Abs((int)(ey - off_y))) * 10);
                    node1.inOpenList = true;
                    this.openList.Push(node1);
                }
                else
                {
                    int num1 = father.g + price;
                    if (node1.g > num1)
                    {
                        node1.father = father;
                        node1.g = num1;
                        this.openList.ModifValue(node1, node1.g + ((Math.Abs((int)(ex - off_x)) + Math.Abs((int)(ey - off_y))) * 10));
                    }
                }
            }
            return true;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void UnBlock(int x, int y)
        {
            this.map[x, y].block = false;
        }
    }
}
